
/*
* LSystem string generator
* Copyright (C) 2010 James Holland
*
*         This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/
import java.io.*;
import java.lang.*;
import java.util.*;
public class LSystem
{
	public static String
	axiom; public static
	int angleDegrees;
		
	// We time our program for a fancy summary
	private static long
	startTimeOfProgram; private
	static long endTime;
	
	private static long
	lapTimeAtThisPoint; public
	
	// The verbosity level determines whether detailed information is 
	// printed to the console
    static boolean
    verbosity = false;
    
    static void quit(int
    errorCode)
    /*
     * This function handles quitting. It displays a verbosity quit message, regardless of
     * verbosity. It handles parsing errors, NOT exceptions.
     */
    {
		switch (errorCode)
        {
            case 0:
            vprint("No errors detected.\nQuitting program");
            break;
            case 1:
            System.err.println("Error: The specified input file does not exist.\n  Please check the spelling and try again.");
            break;
            case 2:
            System.err.println("Invalid parameters.\nValid usage: java LSystem <filename> <iterations> [(v)erbose/(s)ilent]");
            break;
            case 3:
            System.err.println("Number of iterations must be a natural number");
            break;
            default:
            break;
        }
        System.exit(errorCode);
    }

    static void vprint(String string)
    /*
     * Output string if verbosity = true
     * Otherwise, output nothing.
     * !NOT FOR ERRORS!
     */
    {
        if (verbosity == true)
        {
            System.out.print(string);
        }
    }
    

    public static void main (String arg[])
    {
		// Start the "timer"
		startTimeOfProgram = System.currentTimeMillis();
		lapTimeAtThisPoint = startTimeOfProgram;
		
		// str is where each line of text from the file is held as a String
        String str;
        
        // n is the number of iterations
        int n = 0;
        
        // inputFile is the raw File type specified on the command-line
        File inputFile = null;
        
        // Test if command-line parameters exist. If not, say so and quit
       if (arg.length < 2) {
		   quit(2);
	   }
	   
	   else {
			// The first command line parameter is the input file
		   inputFile = new File(arg[0]);
		   
			// The second command-line parameter is the number of iterations
		   try {
			    n = Integer.parseInt(arg[1]);
		   } catch (NumberFormatException e) {
			   quit(3);
		   }
		   
		   // The third command-line parameter sets the verbosity level
		   if (arg[2].equals("v")) {
			   verbosity = true;
		   }
	   }
        try
        {
			// Set up a PrintStream called fileWriter, which will deal with
			// writing to the file at the end
            PrintStream fileWriter = new PrintStream(new FileOutputStream("result.txt"));
            
            // Wrap up the input file in a BufferedReader so we can go through it
            BufferedReader reader = new BufferedReader(new FileReader(inputFile));
            
            // Feed the first line of the input file into str
            str = reader.readLine();
            
            // The first line of the input file is always the angleDegrees
            angleDegrees = Integer.parseInt(str);
            vprint ("Angle: " + angleDegrees + " degrees\n");
            
            // Feed the next line of the input file into str
            str = reader.readLine();
            
            // The second line of the input file is always the axiom
            axiom = str;
            vprint ("Initially, the axiom is " + axiom + "\n\n");
            
            // iterations is NOT the user specified number.
            // rather, it represents the number of the current iteration
            int iterations = 0;
            
            // The next line is always a rule
            str = reader.readLine();
            
            /*
             * We do not know how many rules there are going to be.
             * Here, we mark the current position in the BufferedReader
             * the reset() function will jump back to this line after each iteration
             */
            reader.mark(1024); // it is unlikely the rule will be longer than 1024 characters.
            
            // Do the "Outer loop" (1 iteration) the number of times specified by the user
            while (n > 0)
            {
				// Set up non-cumulative timing for each iteration
				lapTimeAtThisPoint = System.currentTimeMillis();
				
                // Increment the iterations variable and perform the iterate() function (the "Inner loop")
                iterations++;
                axiom = iterate(n, str, axiom, reader); // takes (number of iterations, str, axiom, reader)
                
                // Convert the axiom to uppercase for output
                // This is vital for the working of the program.
                // This line took longer than the rest put together
                axiom = axiom.toUpperCase();
                
                // Print a summary of this pass, if the verbosity value allows
                if (n > 0)
                {	endTime = System.currentTimeMillis();
                    vprint("Completed " + iterations + " iteration(s) in " + (endTime-lapTimeAtThisPoint) + " ms (total " + (endTime - startTimeOfProgram) + " ms)\n");
                }
                
                // Decrement n (number of iterations)
                n--;
            }
            
            // Put the output in the fileWriter (result.txt)
            vprint("\nSaving output... ");
            fileWriter.println(axiom);
            fileWriter.close();
            
            // Summarise the final result
            endTime = System.currentTimeMillis();
            vprint("Done!\n\n---------\n-Summary-\n---------\n");
            vprint("Completed " + iterations + " iterations of \"" + arg[0] + "\" in a total time of " + (endTime - startTimeOfProgram) + " milliseconds.\nFinal string is now stored in result.txt, with a file size of " + (axiom.length()/1024) + " Kb\n\n");
        }
        // Catch disk errors (maybe input file was deleted mid-read?)
        // This isn't in quit(). This is not a bug. See quit()
        catch (IOException e)
        {
            System.out.println ("IO exception =" + e );
        }
        
        Turtle display = new Turtle(axiom);
        
    }
    // end main()
    public static int getAngle()
    {
		return angleDegrees;
	}
    
    // This is the iterate() function that goes over each string and oversees the replacement of strings
    static String iterate(int n, String str, String axiom, BufferedReader reader)
    {
        int i = 0;
        try
        {
            // reader is a BufferedReader so we can jump to a particular line
            // here we go to the first proper line of rules
            reader.reset();
            while (str != null)
            {
                
                i++;
                axiom = replace(axiom, str);
                str = reader.readLine();
            }
            n--;
        }
        catch(java.io.IOException exp)
        {
            exp.printStackTrace();
        }
        return axiom;
    }
    // end iterate()

    static String replace(String axiom, String rule)
    /* 
     * This function takes an axiom, and a rule in the form
     *  x:foo
     *  where x should be replaced by foo
     */
    {
		
		
		// get the character we want to replace
        String replaceChar = Character.toString(rule.charAt(0));
        
        // get what we want to replace it with, in lowercase
        String replaceString = rule.substring(2).toLowerCase();
        
        // do the replacement
        axiom = axiom.replaceAll(replaceChar, replaceString);
        return axiom;
    }

}
// end class LSystem 
